
package com.sjd.wx.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaUserService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.sjd.common.core.domain.AjaxResult;
import com.sjd.common.exception.ServiceException;
import com.sjd.common.utils.StringUtils;
import com.sjd.wx.config.WxMaConfiguration;
import com.sjd.wx.config.WxMaProperties;
import com.sjd.wx.constant.ConfigConstant;
import com.sjd.wx.constant.WxMaConstants;
import com.sjd.wx.domain.SjdMember;
import com.sjd.wx.domain.ThirdSession;
import com.sjd.wx.domain.dto.WxOpenDataDTO;
import com.sjd.wx.mapper.SjdMemberMapper;
import com.sjd.wx.service.WxUserService;
import com.sjd.wx.utils.ThirdSessionHolder;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 微信用户
 *
 * @author joker
 */
@Slf4j
@Service
@AllArgsConstructor
public class WxUserServiceImpl implements WxUserService {

    private final RedisTemplate<Object, Object> redisTemplate;
    private final SjdMemberMapper memberMapper;
    private final WxMaProperties properties;

    @Override
    public SjdMember getByMemberId(Long memberId) {
        return memberMapper.getByMemberId(memberId);
    }

    @Override
    public SjdMember getByOpenId(String openId) {
        return memberMapper.getByOpenId(openId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SjdMember loginMa(String appId, String jsCode) throws WxErrorException {
        WxMaJscode2SessionResult jscode2session = WxMaConfiguration.getMaService(appId).jsCode2SessionInfo(jsCode);
        SjdMember sjdMember = this.getByOpenId(jscode2session.getOpenid());
        if (sjdMember == null) {
            //新增微信用户
            sjdMember = SjdMember.builder()
                    .appType(ConfigConstant.WX_APP_TYPE_1).openId(jscode2session.getOpenid())
                    .sessionKey(jscode2session.getSessionKey()).unionId(jscode2session.getUnionid())
                    .nickName(StringUtils.getRandomName(7))
                    .headimgUrl(properties.getDefaultUserImg())
                    .build();
            try {
                memberMapper.insert(sjdMember);
            } catch (DuplicateKeyException e) {
                if (Objects.requireNonNull(e.getMessage()).contains("uk_appid_openid")) {
                    sjdMember = this.getByOpenId(sjdMember.getOpenId());
                }
            }
        } else {
            //更新SessionKey
            sjdMember.setAppType(ConfigConstant.WX_APP_TYPE_1);
            sjdMember.setOpenId(jscode2session.getOpenid());
            sjdMember.setSessionKey(jscode2session.getSessionKey());
            sjdMember.setUnionId(jscode2session.getUnionid());
            memberMapper.updateByMemberId(sjdMember);
        }
        return sjdMember;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult saveOrUpdateWxUser(WxOpenDataDTO wxOpenDataDTO) {
        ThirdSession thirdSession = ThirdSessionHolder.getThirdSession();
        String appId = thirdSession.getAppId();
        Long memberId = thirdSession.getMemberId();
        String sessionKey = thirdSession.getSessionKey();
        SjdMember sjdMember = SjdMember.builder().memberId(memberId).build();
        WxMaUserService wxMaUserService = WxMaConfiguration.getMaService(appId).getUserService();
        if (WxOpenDataDTO.DataFlag.MOBILE.equals(wxOpenDataDTO.getDataFlag())) {
            WxMaPhoneNumberInfo phoneNoInfo = wxMaUserService.getPhoneNoInfo(sessionKey, wxOpenDataDTO.getEncryptedData(), wxOpenDataDTO.getIv());
            sjdMember.setPhone(phoneNoInfo.getPurePhoneNumber());
            thirdSession.setMobile(sjdMember.getPhone());
            this.updateThirdSession(thirdSession);
        } else if (WxOpenDataDTO.DataFlag.USERINFO.equals(wxOpenDataDTO.getDataFlag())) {
            if (!wxMaUserService.checkUserInfo(sessionKey, wxOpenDataDTO.getRawData(), wxOpenDataDTO.getSignature())) {
                throw new ServiceException("user check failed！！");
            }
            WxMaUserInfo wxMaUserInfo = wxMaUserService.getUserInfo(sessionKey, wxOpenDataDTO.getEncryptedData(), wxOpenDataDTO.getIv());
            BeanUtil.copyProperties(wxMaUserInfo, sjdMember);
            sjdMember.setHeadimgUrl(wxMaUserInfo.getAvatarUrl());
        } else {
            throw new ServiceException("不支持的数据类型：" + wxOpenDataDTO.getDataFlag());
        }
        memberMapper.updateByMemberId(sjdMember);
        return AjaxResult.success();
    }

    @Override
    public void updateThirdSession(ThirdSession thirdSession) {
        //将3rd_session和用户信息存入redis，并设置过期时间
        String key = WxMaConstants.THIRD_SESSION_BEGIN + ":" + thirdSession.getThirdSessionKey();
        redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(thirdSession), WxMaConstants.TIME_OUT_SESSION, TimeUnit.HOURS);
    }

    @Override
    public String getWxMaAppId() {
        try {
            return properties.getConfigs().get(0).getAppId();
        } catch (Exception e) {
            throw new ServiceException("appid配置有误，请检查");
        }
    }

    @Override
    public List<SjdMember> selectSjdMemberList(SjdMember sjdMember) {
        return memberMapper.selectSjdMemberList(sjdMember);
    }
}
